﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Filter;
using Cofe.Core.Implements;
using Cofe.Core.Property;

namespace Cofe.Data.Utils
{
    public static class DBFilterUtils
    {
        private static Expression<Func<T, KT>> getKeySelector<T, KT>(object property, Type propertyType)
        {
            var mi = typeof(IPropertyExpressionDefinitions).GetMethod("GetPropertyExpression").MakeGenericMethod(
                propertyType);
            var exp = mi.Invoke(CofeServices.PropertyDictionary.GetPropertyDefinition(property).Expressions, new object[] { property })
                as Expression<Func<T, KT>>;

            return exp;
        }

        private static IQueryable<T> processQueryIntenral<T, KT>(IQueryable<T> query, object property,
            Type propertyType, bool ascending, bool firstSort)
        {
            if (firstSort)
            {
                if (ascending)
                    return query.OrderBy(getKeySelector<T, KT>(property, propertyType));
                else return query.OrderByDescending(getKeySelector<T, KT>(property, propertyType));

            }
            else
            {
                if (ascending)
                    return (query as IOrderedQueryable<T>).ThenBy<T, KT>(getKeySelector<T, KT>(property, propertyType));
                else return (query as IOrderedQueryable<T>).ThenByDescending<T, KT>(getKeySelector<T, KT>(property, propertyType));
            }
        }

        private static IQueryable<T> processSortQuery<T>(IQueryable<T> query, object property, bool ascending, bool firstSort)
        {
            Type propType = DecoratorExtension.GetDefaultType(property);

            var mi = typeof(DBFilterUtils).GetMethod("processQueryIntenral",
                BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(typeof(T), propType);
            return (IQueryable<T>)mi.Invoke(null, new object[] { query, property, propType, ascending, firstSort });
        }

        private static IQueryable<T> processSortQuery<T>(IQueryable<T> query, IEnumerable<Tuple<object, bool>> properties)
        {
            bool isFirst = true;
            foreach (var prop in properties)
            {
                query = processSortQuery<T>(query, prop.Item1, prop.Item2, isFirst);
                isFirst = false;
            }
            return query;
        }

        public static IEnumerable<T> processPaginate<T>(IEnumerable<T> original, PaginateOptionFilter paginateLookup)
        {
            return original.Skip(paginateLookup.Skip).Take(paginateLookup.ItemsPerPage);
        }

        public static IQueryable<T> processPaginate<T>(IQueryable<T> original, PaginateOptionFilter paginateLookup)
        {
            return original.Skip(paginateLookup.Skip).Take(paginateLookup.ItemsPerPage);
        }

        public static IQueryable<T> UpdateOptionFilterExpressions<T>(IQueryable<T> original, IOptionFilter[] optionFilters, bool handleSort, bool handlePaginate)
        {
            var paginateLookup = optionFilters.FirstOrDefault(lf => lf is PaginateOptionFilter) as PaginateOptionFilter;            

            var expression = original;

            if (handlePaginate || handleSort)
            {
                if (optionFilters.Any(lf => lf is SortResultOptionFilter))
                {
                    var properties = 
                        optionFilters.Where(lf => lf is SortResultOptionFilter).Select(lf => lf as SortResultOptionFilter)
                        .Select(srf => new Tuple<object, bool>(srf.SortProperty, srf.SortDirection == SortDirectionType.Ascending));

                    expression = processSortQuery(expression, properties);
                    
                }
                else
                {
                    //Default sort by fullParseName A->Z
                    expression = processSortQuery(expression,
                        new Tuple<object, bool>[] 
                        {
                            new Tuple<object, bool>(CofeProperties.Position, true), 
                            new Tuple<object, bool>(CofeProperties.Type, true), 
                            new Tuple<object, bool>(CofeProperties.Label, true)
                        });
                }
            }
            
            if (handlePaginate && paginateLookup != null)
            {
                expression = processPaginate(expression, paginateLookup);
            }
            return expression;
        }

    }
}
